/*
 * Copyright (c) 2010, VeRSI Consortium
 *   (Victorian eResearch Strategic Initiative, Australia)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the VeRSI, the VeRSI Consortium members, nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package versi.vitro.oaiserviceprovider.service.validation.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.Source;

import org.apache.commons.lang.NotImplementedException;
import org.apache.log4j.Logger;

import versi.vitro.oaiserviceprovider.service.validation.api.ValidationHelper;
import versi.vitro.oaiserviceprovider.service.validation.api.ValidationService;
import versi.vitro.oaiserviceprovider.service.validation.helper.ValidationHelperImpl;

/**
 * Service implementing {@link ValidationService}.
 */
public class ValidationServiceImpl implements ValidationService {

    /** log4j instance. */
    private static final Logger LOGGER = Logger.getLogger(ValidationServiceImpl.class);

    /** Validation Service Helper. */
    private ValidationHelper validationHelper;

    private List<Source> schemasSource;

    /**
     * Default Constructor.
     */
    public ValidationServiceImpl() {
	this(new ValidationHelperImpl(), new ArrayList<Source>());
    }

    /**
     * Constructor.
     * 
     * @param validationHelper
     *            Validation Service Helper.
     * @param schemaLocationsList
     *            Class path relative paths to schema files.
     * 
     */
    public ValidationServiceImpl(List<String> schemaLocationsList, ValidationHelper validationHelper) {
	this(validationHelper, new ArrayList<Source>());
	String[] schemaLocations = new String[schemaLocationsList.size()];
	schemaLocations = schemaLocationsList.toArray(schemaLocations);
	addSchemas(schemaLocations);
    }

    /**
     * Constructor.
     * 
     * @param validationHelper
     *            Validation Service Helper.
     * @param schemasSource
     *            List of the schema sources.
     * 
     */
    public ValidationServiceImpl(ValidationHelper validationHelper, List<Source> schemasSource) {
	super();
	this.validationHelper = validationHelper;
	this.schemasSource = schemasSource;
    }

    /** {@inheritDoc} */
    public boolean validateActivity(final byte[] inXML) throws Exception {
	throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public boolean validateCollection(final byte[] inXML) throws Exception {
	throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public boolean validateParty(final byte[] inXML) throws Exception {
	throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public boolean validateService(final byte[] inXML) throws Exception {
	throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public void addSchemas(final String[] schemaLocations) {
	for (String location : schemaLocations) {
	    File file = createFile(location);
	    schemasSource = validationHelper.addFiletoSchemas(file, schemasSource);
	}
    }

    /** {@inheritDoc} */
    public void deleteSchemas() {
	schemasSource = new ArrayList<Source>();
    }

    /**
     * 
     * @param location
     * @return
     */
    protected File createFile(final String location) {
	File file;
	try {
	    URL resourceUrl = getClass().getResource(location);
	    if (resourceUrl == null) {
		String message = "Could not find schema resource at path: " + location;
		LOGGER.fatal(message);
		throw new RuntimeException(message);
	    }

	    if (LOGGER.isDebugEnabled())
	    {
		LOGGER.debug("Validation service creating file: " + resourceUrl);
	    }
	    
	    final String protocol = resourceUrl.getProtocol();
	    if ("jar".equals(protocol)  || "rsrc".equals(protocol)) {
		file = copyFileContentsToTempFile(location);
	    } else {
		file = new File(resourceUrl.toURI());
	    }

	} catch (URISyntaxException uriSyntaxException) {
	    String message = "Could not convert schema location URL to URI";
	    LOGGER.fatal(message, uriSyntaxException);
	    throw new RuntimeException(message, uriSyntaxException);
	}
	return file;
    }

    /** {@inheritDoc} */
    public boolean validateAgainstSchemas(final byte[] inByte) {

	Source[] schemaArray = new Source[schemasSource.size()];
	schemasSource.toArray(schemaArray);
	boolean validationResult = validationHelper.validateAgainstSchema(inByte, schemaArray);

	// if (LOGGER.isInfoEnabled())
	// {
	LOGGER.info("validationResult is " + validationResult);
	// }

	return validationResult;
    }

    /** {@inheritDoc} */
    public ValidationHelper getValidationHelper() {
	return validationHelper;
    }
    
    private File copyFileContentsToTempFile(String location) {
	InputStream resourceAsStream = null;
	PrintWriter printWriter = null;
	try {
	    resourceAsStream = getClass().getResourceAsStream(location);
	    InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream);
	    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

	    if (LOGGER.isDebugEnabled())
	    {
		LOGGER.debug("Validation service begin copying jar resource to temp file: " + location);
	    }

	    File tempFile = File.createTempFile("oai_service_provider_validation_", null);
	    printWriter = new PrintWriter(tempFile);

	    int intValue;
	    while ((intValue = bufferedReader.read()) != -1) {
		printWriter.write(intValue);
	    }

	    if (LOGGER.isDebugEnabled())
	    {
		LOGGER.debug("Validation service end copying jar resource to temp file: " + tempFile);
	    }

	    return tempFile;
	} catch (IOException ioException) {
	    String message = "Cannot read source resource: " + location;
	    LOGGER.error(message, ioException);
	    throw new RuntimeException(message, ioException);
	} finally {
	    try {
		if (resourceAsStream != null) {
		    resourceAsStream.close();
		}
		if (printWriter != null) {
		    printWriter.close();
		}
	    } catch (IOException ioException) {
		String message = "Cannot close file after writing into temp file: " + location;
		LOGGER.error(message, ioException);
		throw new RuntimeException(message, ioException);
	    }
	}
    }
}
